﻿
using DataService.Core.Extensions;

using DataService.IService;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using DataService.Cache.Data;
using DataService.Core.DataHelper;
using DataService.Core.ModeCode;
using DataService.Core.ServiceDTO;
using DataService.Core.Common;
using DataService.Data;

namespace DataService.Main
{
    // 若使用多线程操作文件数据库；需要考虑在业务层加锁。
    // sqlite(不支持并发(多线程)写，支持一写多读)：文件级数据库，（sqlite可以使用多数据库连接，来操作同一份sqlite数据库）。
    // sqlserver:支持多写，多读；
    //什么是多线程(并发)操作：可以理解成 一个线程一个并发；多线程使用同一个数据库连接操作数据库即可以理解为多并发。
    //多数据库连接操作：即每个数据库操作使用的是各自的数据库连接
    public static class DAL
    {

        private static readonly object _tableLocker = new object();

        static DAL()
        {
            //优化时，这里配置数据库连接等设置
        }
        /// <summary>
        /// 添加一条数据；
        /// </summary>
        public static int Insert<T>(this T t, IDatabaseProvider tagertProvider) where T : class, new()
        {
            //检查 T是否属于 tagertProvider.DBType的数据库下
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            string stuffName = string.Empty;
            if (string.IsNullOrEmpty(tagertProvider.TableSuffix)&& tagertProvider.IsEnable)
            {
                stuffName = InitTable<T>(t, tagertProvider);
            }
            else
            {
                stuffName = tagertProvider.TableSuffix;
            }
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }

            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            return remoteDB.Insert<T>(t, stuffName, conn, tran);

        }
        /// <summary>
        /// 插入批量数据
        /// </summary>
        public static int Insert<T>(this List<T> t, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            string stuffName = string.Empty;
            if (string.IsNullOrEmpty(tagertProvider.TableSuffix) && tagertProvider.IsEnable)
            {
                stuffName = InitTable<T>(t[0], tagertProvider);
            }
            else
            {
                stuffName = tagertProvider.TableSuffix;
            }
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            return remoteDB.Insert<T>(t, stuffName, conn, tran);
        }

        /// <summary>
        /// 快速插入批量数据
        /// </summary>
        public static int BeatchInsert<T>(this List<T> data, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            Stopwatch stopwatch = new Stopwatch();
            string stuffName = string.Empty;
            if (string.IsNullOrEmpty(tagertProvider.TableSuffix) && tagertProvider.IsEnable)
            {
                stuffName = InitTable<T>(data[0], tagertProvider);
            }
            else
            {
                stuffName = tagertProvider.TableSuffix;
            }
            int rowCount = 0;
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var conn = tagertProvider.DbConnection;

            DbTransaction tran = tagertProvider.Transaction;
            stopwatch.Restart();
            rowCount = remoteDB.BeatchInsert<T>(data, stuffName, conn, tran);
            stopwatch.Stop();
            long ms = stopwatch.ElapsedMilliseconds;
            Console.WriteLine($"快速插入批量数据耗时：{stopwatch.ElapsedMilliseconds}毫秒，准备耗时：{ms}");

            return rowCount;

        }

        /// <summary>
        /// 更新一条数据（默认更新所有字段）
        /// </summary>
        /// <returns>更新条数</returns>
        public static int Update<T>(this T t, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            string stuffName = string.Empty;
            if (string.IsNullOrEmpty(tagertProvider.TableSuffix) && tagertProvider.IsEnable)
            {
                stuffName = InitTable<T>(t, tagertProvider);
            }
            else
            {
                stuffName = tagertProvider.TableSuffix;
            }
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            return remoteDB.Update<T>(t, stuffName, conn, tran);
        }

        /// <summary>
        /// 更新一条数据
        /// </summary>
        /// <param name="fieldFunc">需要更新的字段，例：x=>new{x.字段1, x.字段2}</param>
        /// <returns>更新条数</returns>
        public static int Update<T>(this T t, Expression<Func<T, object>> fieldFunc, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            string stuffName = string.Empty;
            if (string.IsNullOrEmpty(tagertProvider.TableSuffix) && tagertProvider.IsEnable)
            {
                stuffName = InitTable<T>(t, tagertProvider);
            }
            else
            {
                stuffName = tagertProvider.TableSuffix;
            }
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            return remoteDB.Update<T>(t, fieldFunc, stuffName, conn, tran);
        }


        /// <summary>
        /// 批量更新（默认更新所有字段）
        /// </summary>
        /// <returns>更新条数</returns>
        public static int BeatchUpdate<T>(IEnumerable<T> data, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            var dataList = data.ToList();
            string stuffName = string.Empty;
            if (string.IsNullOrEmpty(tagertProvider.TableSuffix) && tagertProvider.IsEnable)
            {
                stuffName = InitTable<T>(dataList[0], tagertProvider);
            }
            else
            {
                stuffName = tagertProvider.TableSuffix;
            }
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            return remoteDB.BeatchUpdate<T>(dataList, stuffName, conn, tran);
        }

        /// <summary>
        /// 批量更新
        /// </summary>
        /// <param name="fieldFunc">需要更新的字段</param>
        /// <returns>更新条数</returns>
        public static int BeatchUpdate<T>(this IEnumerable<T> data, Expression<Func<T, object>> fieldFunc, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            var dataList = data.ToList();
            string stuffName = string.Empty;
            if (string.IsNullOrEmpty(tagertProvider.TableSuffix) && tagertProvider.IsEnable)
            {
                stuffName = InitTable<T>(dataList[0], tagertProvider);
            }
            else
            {
                stuffName = tagertProvider.TableSuffix;
            }
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            return remoteDB.BeatchUpdate<T>(dataList, fieldFunc, stuffName, conn, tran);
        }
        /// <summary>
        /// 删除数据
        ///支持In查找
        /// Guid[] tempArray=new Guid[]{值1,值2}
        /// string[] tempArray=new string[]{值1,值2}
        /// 例：Delete<T>(x => tempArray.Contains(x.字段)|| tempArray1.Contains(x.字段));
        /// </summary>
        /// <returns>删除条数</returns>
        public static int Delete<T>(Expression<Func<T, bool>> whereFunc, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);
            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            // string stuffName = DataBaseHelper.CreateTableNameFlag();
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            return remoteDB.Delete<T>(whereFunc, tagertProvider.TableSuffix, conn, tran);
        }

        /// <summary>
        /// 返回行数，支持In查找
        /// Guid[] tempArray=new Guid[]{值1,值2}
        /// string[] tempArray=new string[]{值1,值2}
        /// </summary>
        /// <param name="whereFunc">ScalarCount<T>(x => tempArray.Contains(x.字段)|| tempArray1.Contains(x.字段)||x.字段=值)</param>
        public static int ScalarCount<T>(Expression<Func<T, bool>> whereFunc, IDatabaseProvider tagertProvider) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);

            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;

            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            return remoteDB.ScalarCount<T>(whereFunc, tagertProvider.TableSuffix, conn, tran, null, CommandType.Text);
        }
        /// <summary>返回列表 </summary>
        /// <param name="whereFunc">
        /// Guid[] tempArray=new Guid[]{值1,值2}
        ///例：Query<T>(x => tempArray.Contains(x.字段)&&x.字段2=值2||x.字段3=值3);
        /// <param name="fieldFunc">查询指定字段</param>
        /// <returns></returns>
        public static IEnumerable<T> Query<T>(Expression<Func<T, bool>> whereFunc, IDatabaseProvider tagertProvider, Expression<Func<T, object>> fieldFunc = null) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);

            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;

            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var query = remoteDB.Query<T>(whereFunc, tagertProvider.TableSuffix, conn, tran, null, fieldFunc);
            stopwatch.Stop();
            Console.WriteLine("查询列表耗时：" + stopwatch.ElapsedMilliseconds + "毫秒");
            return query;
        }
        /// <summary>
        /// 异步返回列表
        /// </summary>
        /// <param name="whereFunc"> Guid[] tempArray=new Guid[]{值1,值2} 示例 Query<T>(x => tempArray.Contains(x.字段)&&x.字段2=值2||x.字段3=值3);</param>
        /// <param name="suffix">分表后缀</param>
        /// <param name="fieldFunc">查询指定字段</param>
        public static Task<IEnumerable<T>> QueryAsync<T>(Expression<Func<T, bool>> whereFunc, IDatabaseProvider tagertProvider, Expression<Func<T, object>> fieldFunc = null) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);

            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var query = remoteDB.QueryAsync<T>(whereFunc, tagertProvider.TableSuffix, conn, tran, null, fieldFunc);
            stopwatch.Stop();
            Console.WriteLine("查询异步列表耗时：" + stopwatch.ElapsedMilliseconds + "毫秒");
            return query;
        }


        /// <summary>返回 指定表  单个实体</summary>
        /// <param name="suffix">分表后缀</param>
        /// <param name="fieldFunc">查询指定字段</param>
        /// <returns>查不到返回null</returns>
        public static T QueryModel<T>(Expression<Func<T, bool>> whereFunc, IDatabaseProvider tagertProvider, Expression<Func<T, object>> fieldFunc = null) where T : class, new()
        {
            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);

            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            if (tagertProvider.IsEnable)
            {
                remoteDB.ModeFilePath = GetModelPath();
            }
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var model = remoteDB.QueryModel<T>(whereFunc, tagertProvider.TableSuffix, conn, tran, null, fieldFunc);
            stopwatch.Stop();
            Console.WriteLine("查询实体耗时：" + stopwatch.ElapsedMilliseconds + "毫秒");
            return model;
        }


        /// <summary>
        ///查询分页；排序字段优先使用主键、其次是索引字段；默认查询所有字段
        /// </summary>
        /// <param name="pageSize">每页条数</param>
        /// <param name="pageIndex">当前页码</param>
        /// <param name="asc">正序排序；提示：排序字段尽量使用索引字段；建议使用主键</param>
        /// <param name="desc">倒序排序；提示：排序字段尽量使用索引字段；建议使用主键</param>
        /// <param name="fieldFunc">查询指定字段</param>
        public static IEnumerable<T> QueryPage<T>(Expression<Func<T, bool>> whereFunc, RequestPageDTO requestPageDTO, IDatabaseProvider tagertProvider, ref ResultPageDTO resultPageDTO, Expression<Func<T, object>> fieldFunc = null) where T : class, new()
        {
            //判断页码是否大于等于1
            //客户端判断若上次请求页码和本次请求页码一样；则不请求接口
            //页码要小于等于最大页码
            //每页条数需要和上次一致，如此处 请求每页条数和上次不一样，则PageIndex从1开始
            //查询字段一定要包含主键，判断fieldFunc不为空时，是否包含主键


            //排序字段暂时赋值为空
            // string[] asc = null;
            // string[] desc = null;
            //RequestPageDTO requestPageDTO = new RequestPageDTO();
            // requestPageDTO.PageIndex = pageIndex;
            // requestPageDTO.PageSize = pageSize;
            // requestPageDTO.Ascending = asc;
            // requestPageDTO.Descending = desc;


            VerifyConnString(tagertProvider.ConnectionString, tagertProvider.DBType);

            //需要判断ParamNextStartId和ParamUpStartId 是否是雪花Id


            if (requestPageDTO.ParamNextStartId > 0)
            {
                var f = new Snowflake();
                f.TryParse(requestPageDTO.ParamNextStartId, out long ms, out int workId, out int seque);
                if (ms == 0)
                {
                    throw new ArgumentException("ID值错误");
                }
            }
            if (requestPageDTO.ParamUpStartId > 0)
            {
                var f = new Snowflake();
                f.TryParse(requestPageDTO.ParamUpStartId, out long ms, out int workId, out int seque);
                if (ms == 0)
                {
                    throw new ArgumentException("ID值错误");
                }
            }
            if (requestPageDTO.PageIndex == 0) requestPageDTO.PageIndex = 1;
            if (requestPageDTO.PageSize == 0) requestPageDTO.PageSize = 30;

            requestPageDTO.Schema = tagertProvider.Schema;

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);

            var conn = tagertProvider.DbConnection;
            var tran = tagertProvider.Transaction;
            remoteDB.ModeFilePath = GetModelPath();
            remoteDB.ConnectionString = tagertProvider.ConnectionString;
            var query = remoteDB.QueryPage<T>(whereFunc, requestPageDTO, tagertProvider.TableSuffix, conn, tran, null, fieldFunc, ref resultPageDTO);
            stopwatch.Stop();
            Console.WriteLine("查询分页耗时：" + stopwatch.ElapsedMilliseconds + "毫秒,当前线程:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
            return query;
        }

        #region 创建初始化表名
        private static string InitTable<T>(T t, IDatabaseProvider tagertProvider)
        {
            string stuffName = string.Empty;
            var remoteDB = DBMangerFactory.Create(tagertProvider.DBType);

            bool isFlag = remoteDB.IsExistsTable<T>(t, ref stuffName);
            if (isFlag)
            {
                return stuffName;
            }
            lock (_tableLocker)
            {
                //多线程同时进入，有可能会查不到表名称，这里再查一次
                isFlag = remoteDB.IsExistsTable<T>(t, ref stuffName);
                if (isFlag)
                {
                    return stuffName;
                }

                //优化阶段，创建表放在一个单独的模块，此处不再单独打开连接(不再调用databaseProvider1.DbConnection)创建表
                remoteDB.ModeFilePath = GetModelPath();
                remoteDB.ConnectionString = tagertProvider.ConnectionString;
                var pkIds = remoteDB.GetModelPkFields<T>();
                if (pkIds == null || pkIds.Count == 0)
                {
                    return String.Empty;
                }
                string pkId = pkIds[0];
                object objSown = MemberInfoHelper.GetPropertyValue(t, pkId);
                stuffName = GetTablesuffix(long.Parse(objSown.ToString()));
                string modelTableName = typeof(T).Name;
                var dbProvider = tagertProvider.DatabaseProviderType.CreateInstance() as IDatabaseProvider;
                using (var databaseProvider1 = dbProvider)
                {
                    databaseProvider1.ConnectionString = tagertProvider.ConnectionString;
                    remoteDB.CreatTableSchema(modelTableName, stuffName, tagertProvider.DatabaseName, tagertProvider.Schema, databaseProvider1.DbConnection, null);
                }

                return stuffName;
            }

        }
        /// <summary>
        /// 默认模型文件
        /// </summary>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static string GetModelPath()
        {
            string filePath = Path.Combine(FilePathHelper.BasePath, "Model.db");

            if (!File.Exists(filePath))
            {
                filePath = Path.Combine(FilePathHelper.BasePath, "Model\\Model.db");
                if (!File.Exists(filePath))
                {
                    throw new ArgumentException("模型文件不存在");
                }
            }
            return filePath;
        }
        /// <summary>
        /// 获取分表后缀
        /// </summary>
        /// <param name="snowId"></param>
        /// <returns></returns>
        public static string GetTablesuffix(long snowId)
        {
            string stuffName = DataBaseHelper.CreateTableNameFlag(snowId);
            return stuffName;
        }
        #endregion

        private static bool VerifyConnString(string connStr, DatabaseType DBType)
        {
            if (DBType== DatabaseType.SQLite)
            {
                string[] tempArray = connStr.Split(";");
                if (tempArray.Length == 0)
                {
                    var stackFrames = StackFrameHelper.GetStackFrame();
                    string frameStr = string.Join(">", stackFrames);
                    DataService.Core.Log.TraceLog.WriteLine($"线程：{Thread.CurrentThread.ManagedThreadId},【{DBType}】数据库连接格式错误：{connStr}，调用顺序：{frameStr}", "数据库");
                    throw new ArgumentException($"【{DBType}】数据库连接格式错误，没有检查到分号");
                }
                string[] dbConnFirstPartStr = tempArray[0].Split('=');
                if (dbConnFirstPartStr.Length == 0)
                {
                    var stackFrames = StackFrameHelper.GetStackFrame();
                    string frameStr = string.Join(">", stackFrames);
                    DataService.Core.Log.TraceLog.WriteLine($"线程：{Thread.CurrentThread.ManagedThreadId},【{DBType}】数据库连接格式错误：{connStr}，调用顺序：{frameStr}", "数据库");
                    throw new ArgumentException($"【{DBType}】数据库连接格式错误，没有检查到等号");
                }
                string dbFilePath = dbConnFirstPartStr[1];
                if (!File.Exists(dbFilePath))
                {
                    var stackFrames = StackFrameHelper.GetStackFrame();
                    string frameStr = string.Join(">", stackFrames);
                    DataService.Core.Log.TraceLog.WriteLine($"线程：{Thread.CurrentThread.ManagedThreadId},【{DBType}】：{connStr}，找不到物理路径：{dbFilePath}，调用顺序：{frameStr}", "数据库");
                    throw new ArgumentException($"【{DBType}】数据库连接格式错误，找不到DB数据库物理路径");
                }
               
            }
            return true;
        }

    }
}
